package com.xunlai.infra.permission.business.service.impl;

import com.xunlai.infra.permission.business.converter.PosterConverter;
import com.xunlai.infra.permission.business.converter.RouteConverter;
import com.xunlai.infra.permission.business.dao.entity.*;
import com.xunlai.infra.permission.business.dao.entity.vo.PosterTemplateUseVo;
import com.xunlai.infra.permission.business.dao.repository.*;
import com.xunlai.infra.permission.business.service.PosterService;
import com.xunlai.infra.permission.business.vo.poster.*;
import com.xunlai.infra.permission.business.vo.route.RouteSubjectListVo;
import com.xunlai.infra.permission.business.vo.route.RouteTypeCreateVo;
import com.xunlai.infra.permission.business.vo.route.RouteTypeListVo;
import com.xunlai.infra.permission.business.vo.route.RouteTypeQueryVo;
import com.xunlai.infra.permission.common.exception.ExceptionCodeEnum;
import com.xunlai.infra.permission.common.exception.PermissionBusinessException;
import com.xunlai.infra.permission.common.security.TenantUser;
import com.xunlai.infra.permission.common.vo.BasePageRespVo;
import com.xunlai.infra.permission.common.vo.BaseResponse;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/**
 * @author liang
 * @date 8/7/23 9:51 PM
 */
@Service
public class PosterServiceImpl implements PosterService {

    private PosterTemplateRepository posterTemplateRepository;

    private PosterFavoriteRepository posterFavoriteRepository;

    private PosterTypeRepository posterTypeRepository;

    private PosterRepository posterRepository;

    private MemberRepository memberRepository;

    @Override
    public Long createPosterTemplate(PosterTemplateCreateVo posterTemplateCreateVo) {
        PosterTemplate posterTemplate = PosterConverter.INSTANCE.convertCreateVo2TemplateEntity(posterTemplateCreateVo);
        posterTemplate.setPosterTemplateNo(UUID.randomUUID().toString().replace("-", ""));
        PosterTemplate save = posterTemplateRepository.save(posterTemplate);
        return save.getId();
    }

    @Override
    public void updatePosterTemplate(Long id, PosterTemplateCreateVo posterTemplateCreateVo) {
        Optional<PosterTemplate> posterTemplateOptional = posterTemplateRepository.findById(id);
        if (posterTemplateOptional.isEmpty()) {
            return;
        }
        PosterTemplate posterTemplate = posterTemplateOptional.get();
        BeanUtils.copyProperties(posterTemplateCreateVo, posterTemplate);
        posterTemplateRepository.save(posterTemplate);
    }

    @Override
    public void deletePosterTemplate(Long id) {
        Optional<PosterTemplate> posterTemplateOptional = posterTemplateRepository.findById(id);
        if (posterTemplateOptional.isEmpty()) {
            return;
        }
        posterTemplateRepository.deleteById(id);
    }

    @Override
    public Long createPosterFavorite(Long userId, PosterFavoriteCreateVo posterFavoriteCreateVo) {
        PosterFavorite posterFavorite = PosterConverter.INSTANCE.convertCreateVo2FavoriteEntity(posterFavoriteCreateVo);
        posterFavorite.setUserId(userId);
        PosterFavorite save = posterFavoriteRepository.save(posterFavorite);
        return save.getId();
    }

    @Override
    public Long createPosterType(PosterTypeCreateVo routeTypeCreateVo) {
        PosterType routeType = PosterConverter.INSTANCE.convertCreateVo2PosterTypeEntity(routeTypeCreateVo);
        PosterType save = posterTypeRepository.save(routeType);
        return save.getId();
    }

    @Override
    public BasePageRespVo<PosterTypeListVo> listPosterType(PosterTypeQueryVo routeTypeQueryVo, Integer pageNum, Integer pageSize) {
        PosterType routeType = PosterConverter.INSTANCE.convertQueryVo2PosterTypeEntity(routeTypeQueryVo);
        pageNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
        int size = (pageSize == null || pageSize <= 0) ? 10 : pageSize;
        Page<PosterType> routeTypePage = posterTypeRepository.findAll(Example.of(routeType, ExampleMatcher.matching().withIgnoreNullValues().withMatcher("typeName", ExampleMatcher.GenericPropertyMatcher::contains)), PageRequest.of(pageNum - 1, size));
        List<PosterTypeListVo> routeTypeListVos = routeTypePage.getContent().stream().map(PosterConverter.INSTANCE::convertPosterTypeEntity2ListVo).toList();
        return new BasePageRespVo<>(routeTypeListVos, pageNum, size, routeTypePage.getTotalElements(), routeTypePage.getTotalPages());

    }

    @Override
    public List<PosterTypeListVo> listAllPosterType() {
        Iterable<PosterType> all = posterTypeRepository.findAll();
        return StreamSupport.stream(all.spliterator(), false).map(PosterConverter.INSTANCE::convertPosterTypeEntity2ListVo).toList();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updatePosterType(Long id, PosterTypeCreateVo routeTypeCreateVo) {
        String typeName = routeTypeCreateVo.getTypeName();
        if (StringUtils.isBlank(typeName)) {
            return;
        }
        Optional<PosterType> routeTypeOptional = posterTypeRepository.findById(id);
        if (routeTypeOptional.isEmpty()) {
            return;
        }
        PosterType routeType = routeTypeOptional.get();
        routeType.setTypeName(typeName);
        posterTypeRepository.save(routeType);
    }

    @Override
    public BasePageRespVo<PosterTemplateListVo> listPosterTemplate(Long userId, Long posterType, String posterName, Boolean favorite, Integer pageNum, Integer pageSize) {
        pageNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
        int size = (pageSize == null || pageSize <= 0) ? 10 : pageSize;
        PageRequest pageRequest = PageRequest.of(pageNum - 1, size);
        Page<PosterTemplate> posterTemplatePage;
        List<PosterTemplateListVo> posterTemplateListVos;
        Iterable<PosterFavorite> favoriteIterable = posterFavoriteRepository.findAll(Example.of(PosterFavorite.builder().userId(userId).build(),ExampleMatcher.matching().withIgnoreNullValues()));
        Set<Long> posterIds = StreamSupport.stream(favoriteIterable.spliterator(), false).map(PosterFavorite::getPosterId).collect(Collectors.toSet());
        if (favorite == null) {
            final Set<Long> templateIdSet = new HashSet<>();
            Example<PosterTemplate> example = Example.of(PosterTemplate.builder().posterType(posterType).posterName(posterName).build(), ExampleMatcher.matching().withIgnoreNullValues().withMatcher("posterName", ExampleMatcher.GenericPropertyMatcher::contains));
            posterTemplatePage = posterTemplateRepository.findAll(example, pageRequest);
            posterTemplateListVos = posterTemplatePage.getContent().stream().map(posterTemplate -> {
                PosterTemplateListVo posterTemplateListVo = PosterConverter.INSTANCE.convertEntity2ListVo(posterTemplate);
                posterTemplateListVo.setCollected(posterIds.contains(posterTemplate.getId()));
                templateIdSet.add(posterTemplate.getId());
                return posterTemplateListVo;
            }).toList();
            if(CollectionUtils.isNotEmpty(templateIdSet)){
                List<PosterTemplateUseVo> posterTemplateUseVos = posterRepository.selectTemplateCountByTemplateIn(templateIdSet);
                if(!posterTemplateUseVos.isEmpty()){
                    Map<Long, Long> countMap = posterTemplateUseVos.stream().collect(Collectors.toMap(u -> u.getPosterTemplate(), u -> u.getTemplateCount()));
                    posterTemplateListVos.forEach(posterTemplateListVo -> {
                        Long count = countMap.get(posterTemplateListVo.getId());
                        posterTemplateListVo.setUseNumber(count == null ? 0 : count.intValue());
                    });
                }
            }
            return new BasePageRespVo<>(posterTemplateListVos, pageNum, size, posterTemplatePage.getTotalElements(), posterTemplatePage.getTotalPages());
        }
        posterName = posterName == null ? "" : posterName;
        if (favorite) {
            if (posterType == null) {
                posterTemplatePage = posterTemplateRepository.findAllByIdInAndPosterNameContains(posterIds, posterName, pageRequest);
            } else {
                posterTemplatePage = posterTemplateRepository.findAllByIdInAndPosterTypeAndPosterNameContains(posterIds, posterType, posterName, pageRequest);
            }
            posterTemplateListVos = posterTemplatePage.getContent().stream().map(posterTemplate -> {
                PosterTemplateListVo posterTemplateListVo = PosterConverter.INSTANCE.convertEntity2ListVo(posterTemplate);
                posterTemplateListVo.setCollected(true);
                return posterTemplateListVo;
            }).toList();
            return new BasePageRespVo<>(posterTemplateListVos, pageNum, size, posterTemplatePage.getTotalElements(), posterTemplatePage.getTotalPages());
        }
        if (posterType == null) {
            posterTemplatePage = posterTemplateRepository.findAllByIdNotInAndPosterNameContains(posterIds, posterName, pageRequest);
        } else {
            posterTemplatePage = posterTemplateRepository.findAllByIdNotInAndPosterTypeAndPosterNameContains(posterIds, posterType, posterName, pageRequest);
        }
        posterTemplateListVos = posterTemplatePage.getContent().stream().map(posterTemplate -> {
            PosterTemplateListVo posterTemplateListVo = PosterConverter.INSTANCE.convertEntity2ListVo(posterTemplate);
            posterTemplateListVo.setCollected(false);
            return posterTemplateListVo;
        }).toList();
        return new BasePageRespVo<>(posterTemplateListVos, pageNum, size, posterTemplatePage.getTotalElements(), posterTemplatePage.getTotalPages());
    }

    @Override
    public PosterTemplateVo getPosterTemplate(Long id) {
        Optional<PosterTemplate> posterTemplateOptional = posterTemplateRepository.findById(id);
        if (posterTemplateOptional.isEmpty()) {
            return null;
        }
        return PosterConverter.INSTANCE.convertTemplateEntity2Vo(posterTemplateOptional.get());
    }

    @Override
    public void deletePosterFavorite(Long userId, Long posterId) {
        posterFavoriteRepository.deleteByPosterIdAndUserId(posterId, userId);
    }

    @Override
    public Long createPoster(PosterCreateVo posterCreateVo) throws PermissionBusinessException {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Object principal = authentication.getPrincipal();
        if(principal instanceof TenantUser tenantUser){
            Long userId = Long.valueOf(tenantUser.getId());
            Optional<Member> memberOptional = memberRepository.findByAccountId(userId);
            boolean isVip = false;
            if(memberOptional.isPresent()){
                Member member = memberOptional.get();
                LocalDateTime vipStartTime = member.getVipStartTime();
                LocalDateTime vipEndTime = member.getVipEndTime();
                isVip = vipStartTime != null && vipEndTime != null && vipEndTime.isAfter(LocalDateTime.now());
            }
            if(!isVip){
                Integer count = posterRepository.countByCreateUser(userId);
                if(count >= 10){
                    throw new PermissionBusinessException(ExceptionCodeEnum.POSTER_OVER_LIMIT);
                }
            }
        }
        Poster poster = PosterConverter.INSTANCE.convertCreateVo2PosterEntity(posterCreateVo);
        Poster save = posterRepository.save(poster);
        return save.getId();
    }

    @Override
    public void deletePoster(Long id) {
        posterRepository.deleteById(id);
    }

    @Override
    public PosterDetailVo getPoster(Long id) {
        Optional<Poster> posterOptional = posterRepository.findById(id);
        if (posterOptional.isEmpty()) {
            return null;
        } else {
            Poster poster = posterOptional.get();
            return PosterConverter.INSTANCE.convertPosterEntity2DetailVo(poster);
        }
    }

    @Override
    public PosterDetailVo getPosterByPosterNo(String posterNo) {
        Optional<Poster> posterOptional = posterRepository.findByPosterNo(posterNo);
        if (posterOptional.isEmpty()) {
            return null;
        } else {
            Poster poster = posterOptional.get();
            return PosterConverter.INSTANCE.convertPosterEntity2DetailVo(poster);
        }
    }

    @Override
    public void updatePoster(Long id, PosterCreateVo posterCreateVo) {
        Optional<Poster> posterOptional = posterRepository.findById(id);
        if (posterOptional.isEmpty()) {
            return;
        }
        Poster poster = posterOptional.get();
        BeanUtils.copyProperties(posterCreateVo, poster);
        posterRepository.save(poster);
    }



    @Override
    public BasePageRespVo<PosterListVo> listPoster(Long userId, Long posterType, String posterName, Integer pageNum, Integer pageSize) {
        pageNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
        int size = (pageSize == null || pageSize <= 0) ? 10 : pageSize;
        PageRequest pageRequest = PageRequest.of(pageNum - 1, size);
        Poster poster = Poster.builder().posterType(posterType).posterName(posterName).build();
        poster.setCreateUser(userId);
        Page<Poster> posterPage = posterRepository.findAll(Example.of(poster, ExampleMatcher.matching().withIgnoreNullValues().withMatcher("posterName", ExampleMatcher.GenericPropertyMatcher::contains)), pageRequest);
        List<PosterListVo> posterListVos = posterPage.getContent().stream().map(PosterConverter.INSTANCE::convertPosterEntity2ListVo).toList();
        return new BasePageRespVo<>(posterListVos, pageNum, pageSize, posterPage.getTotalElements(), posterPage.getTotalPages());
    }

    @Autowired
    public void setPosterTemplateRepository(PosterTemplateRepository posterTemplateRepository) {
        this.posterTemplateRepository = posterTemplateRepository;
    }

    @Autowired
    public void setPosterFavoriteRepository(PosterFavoriteRepository posterFavoriteRepository) {
        this.posterFavoriteRepository = posterFavoriteRepository;
    }

    @Autowired
    public void setPosterTypeRepository(PosterTypeRepository posterTypeRepository) {
        this.posterTypeRepository = posterTypeRepository;
    }

    @Autowired
    public void setPosterRepository(PosterRepository posterRepository) {
        this.posterRepository = posterRepository;
    }

    @Autowired
    public void setMemberRepository(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}
